
package com.smallrig.mall.template.http;

import java.lang.reflect.*;
import java.util.List;
import java.util.Objects;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.houbb.asm.tool.reflection.AsmMethods;
import feign.MethodMetadata;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

@Data
public class ExchangeClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

	private Class<?> type;

	private String value;

	private String url;

	private String contextId;

	private ApplicationContext applicationContext;


	@Override
	public void afterPropertiesSet() {
		Assert.hasText(contextId, "Context id must be set");
		Assert.hasText(value, "value must be set");
	}



	@Override
	public Object getObject() throws Exception {
		return getTarget();
	}


	<T> T getTarget() {
		return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
				new JdkInvocationHandler());
	}


	private class JdkInvocationHandler implements InvocationHandler {

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			if("toString".equals(method.getName())){
				return show();
			}
			Pair<String, RequestMethod> mappingUrl = getMappingUrl(method);

			String reqParams = getReqParams(args,mappingUrl.getRight(),method);

			Type respType = null;
			Type genericReturnType = method.getGenericReturnType();
			if (genericReturnType instanceof ParameterizedType) {
				Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
				respType = actualTypeArguments[0];
			}
			//http调用 todo
			return null;
		}
	}

	private String getReqParams(Object[] args,RequestMethod requestMethod,Method method){
		if(null==args||args.length==0){
			return null;
		}
		if(RequestMethod.POST.equals(requestMethod)){
			return JSON.toJSONString(args[0]);
		}
//		List<String> param =  AsmMethods.getParamNamesByAsm(method);
//		ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
//		String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
//		SpringMvcContract springMvcContract = new SpringMvcContract();
//		MethodMetadata methodMetadata = springMvcContract.parseAndValidateMetadata(type, method);

		Parameter[] parameters = method.getParameters();

		StringBuffer sb = new StringBuffer();
		for(int i=0;i<args.length;i++){
			Parameter parameter = parameters[i];
			RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
			if(null==requestParam){
				throw new RuntimeException("get请求参数必须指定RequestParam");
			}
			String parameterName = requestParam.value();
			if(StringUtils.isBlank(parameterName)){
				throw new RuntimeException("get请求参数必须指定RequestParam的value");
			}
			Object arg = args[i];
			sb.append(parameterName).append("=").append(arg).append("&");
		}
		return sb.toString().substring(0,sb.length()-1);
	}


	private Pair<String, RequestMethod> getMappingUrl(Method method){
		GetMapping getMapping = method.getAnnotation(GetMapping.class);
		if(null!=getMapping){
			return ImmutablePair.of(getMapping.value()[0],RequestMethod.GET);
		}
		PostMapping postMapping = method.getAnnotation(PostMapping.class);
		if(null!=postMapping){
			return ImmutablePair.of(postMapping.value()[0],RequestMethod.POST);
		}
		RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
		if(null!=requestMapping){
			return ImmutablePair.of(requestMapping.value()[0],requestMapping.method()[0]);
		}
		throw new RuntimeException("请添加GetMapping或PostMapping或RequestMapping");
	}
	@Override
	public Class<?> getObjectType() {
		return type;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}



	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.applicationContext = context;
	}


	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		ExchangeClientFactoryBean that = (ExchangeClientFactoryBean) o;
		return Objects.equals(applicationContext, that.applicationContext)
				&& Objects.equals(value, that.value)
				&& Objects.equals(type, that.type) && Objects.equals(url, that.url);
	}

	@Override
	public int hashCode() {
		return Objects.hash(applicationContext, value, type, url);
	}

	public String toString() {
		return show();
	}

	public String show(){
		return new StringBuilder("FeignClientFactoryBean{").append("type=").append(type)
				.append(", ").append("value='").append(value).append("', ")
				.append("url='").append(url).append("', ")
				.toString();
	}

}
